EzHID {
	var <controller;
	*new { | name, index = 0 |
		^super.new.getDev( name, index );
	}
	getDev { | name, index = 0 |
		var valids;
		"EzHID: this next step produces errors, but still succeeds.".postln;
		GeneralHID.buildDeviceList;
		valids = GeneralHID.deviceList;
		if( name.notNil && valids.notNil,
			{ valids = valids.select{ | dev | dev[1].name == name };
				if( valids.size >= index && valids[ index ].notNil, {
					controller = GeneralHID.open( valids[index] );
				} );
			}
		);
		^this;
	}
}

ControlSlot {
	var <>actions;
	var slot;
	*new { | newSlot |
		^super.new.prInit( newSlot );
	}
	prInit { | newSlot |
		actions = Dictionary.new;
		slot = newSlot;
		slot.action = {
			| v |
			actions.do {
				| x |
				x.( v.value );
			}
		}
		^this;
	}
	addAction { | key, lambda |
		actions.put(key, lambda);
		^this;
	}
	remAction { | key |
		actions.removeAt(key);
		^this;
	}
}
			/* class for Logitech Dual Action usb joysticks */
LogJoy {
	var epsilon = 0.003921568627451;
	var <joyStick; // ends up being a GeneralHID device
	var <fronts, <topRights, <topCenters, <joyPushes, <sticks;
	var <x0, <y0, <x1, <y1;
	var <b1, <b2, <b3, <b4, <b5, <b6, <b7, <b8, <b9, <b10, <br, <bl;
	*new { | index = 0, name = 'Logitech Logitech Dual Action' |
		^super.new.assign( index, name );
	}
	assign { | index, name |
		joyStick = EzHID( name, index ).controller;
		if( joyStick.notNil, {
			fronts     = ( 292 .. 295 ).collect
			{ | x | ControlSlot( joyStick.slots[1].at( x ) ) };
			topRights  = ( 288 .. 291 ).collect
			{ | x | ControlSlot( joyStick.slots[1].at( x ) ) };
			topCenters = [ 296,   297 ].collect
			{ | x | ControlSlot( joyStick.slots[1].at( x ) ) };
			joyPushes  = [ 298,   299 ].collect
			{ | x | ControlSlot( joyStick.slots[1].at( x ) ) };
			sticks     =						
			[									
				[ 0, 1 ],						
				[ 2, 5 ]   ].collect( _.collect
					{ | x | ControlSlot( joyStick.slots[3].at( x ) ) } );
			# x0, y0 = sticks[0];
			# x1, y1 = sticks[1];
			# b1, b2, b3, b4 =  topRights;
			# b5, b6, b7, b8 =     fronts;
			# b9, b10        = topCenters;
			# bl, br         =  joyPushes;
		} );
		^this;
	}
}

RelativeMotion {
	var <>prev_v  = 0.5;
	var <>slope   = 12.5663706;   // 4pi
	var <>scale   = 0.0009765625; // 1/1024
	var <>delta_v = 0;
	var <>joystick;
	var <>invert;
	var <>symbol;
	var <>action;
	*new {
		| act, joy, sym, inv = false, slpe = 12.5663706,
		scle = 0.0009765625, prev = 0.5 |
		^super.new.init( prev, slpe, scle, act, joy, sym, inv );
	}
	init { | prev, slpe, scle, act, joy, sym, inv |
		prev_v   =  prev;
		slope    =  slpe;
		scale    =  scle;
		joystick =   joy;
		action   =   act;
		invert   =   inv;
		symbol   =   sym;
		joystick.addAction( symbol,
			{ | v |
				var new_v = if( invert, { ( 1 - v) - 0.5 }, { v - 0.5 } );
				if( ( new_v.abs ) < 0.01,
					{ delta_v = 0 },
					{ delta_v = sinh( new_v * slope ) * scale } );
			};
		);
		^this;
	}
	update { | counter |
		prev_v = ( prev_v + delta_v ).clip( 0, 1 );
		action.value( prev_v, delta_v, counter );
		^this
	}
}
